package ru.ifmo.croak.impl.replication.team1;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.ifmo.croak.api.Message;
import ru.ifmo.croak.api.MessageListener;
import ru.ifmo.croak.api.MessageQuery;
import ru.ifmo.croak.api.QueryRequest;
import ru.ifmo.croak.impl.replication.team1.io.MulticastMessage;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;

class Sender implements Runnable, MessageListener {
    private static final Logger log = LoggerFactory.getLogger(Sender.class);
    private final LocalContext localContext;
    private final MulticastSocket sendingSocket;
    private final InetSocketAddress multicastAddress;

    public Sender(MulticastSocket sendingSocket, LocalContext localContext, InetSocketAddress multicastAddress) {
        this.sendingSocket = sendingSocket;
        this.localContext = localContext;
        this.multicastAddress = multicastAddress;
    }

    public void run() {
        QueryRequest queryRequest = new QueryRequest();
        queryRequest.setLimit(Integer.MAX_VALUE);
        MessageQuery messageQuery = localContext.getQueryService().createQuery(queryRequest);
        messageQuery.setMessageListener(this);
        for (Message message : messageQuery.getMessages()) {
            localContext.putMessageIntoLocalTimeOfArrivalAndUpdateXor(message.getId());
        }
    }

    public void sendSystemMessage(MulticastMessage.MessageType messageType) {
        MulticastMessage multicastMessage = new MulticastMessage(messageType, null, localContext.getSynchronizerTCPPort(), multicastAddress, localContext);
        DatagramPacket pack = multicastMessage.getDatagramPacket();
        try {
            sendingSocket.send(pack);
            log.debug("send system message: {}", messageType);
        } catch (IOException e) {
            log.debug("send system message fail", e);
        }

    }


    public void messageAvailable(Message message) {
        boolean added = localContext.putMessageIntoLocalTimeOfArrivalAndUpdateXor(message.getId());
        if (added) {
            log.debug("broadcasting {}", message);
            MulticastMessage multicastMessage = new MulticastMessage(message, localContext.getSynchronizerTCPPort(), multicastAddress, localContext);
            DatagramPacket pack = multicastMessage.getDatagramPacket();
            try {
                sendingSocket.send(pack);
                log.debug("broadcasted {}", message);
            } catch (IOException e) {
                log.debug("send broadcast message fail", e);
            }
        }
    }

}
